home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / BasicHSVChooserPanel.java < prev    next >
Text File  |  1998-06-30  |  20KB  |  687 lines

  1. /*
  2.  * @(#)BasicHSVChooserPanel.java    1.6 98/04/10
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package com.sun.java.swing.plaf.basic;
  22.  
  23. import com.sun.java.swing.*;
  24. import com.sun.java.swing.event.*;
  25. import com.sun.java.swing.plaf.ComponentUI;
  26. import com.sun.java.swing.plaf.ColorChooserUI;
  27. import java.awt.*;
  28. import java.awt.image.*;
  29. import java.awt.event.*;
  30. import java.beans.PropertyChangeEvent;
  31. import java.beans.PropertyChangeListener;
  32. import java.io.Serializable;
  33.  
  34.  
  35. /**
  36.  * The standard HSV chooser.
  37.  * <p>
  38.  * Warning: serialized objects of this class will not be compatible with
  39.  * future swing releases.  The current serialization support is appropriate 
  40.  * for short term storage or RMI between Swing1.0 applications.  It will
  41.  * not be possible to load serialized Swing1.0 objects with future releases
  42.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  43.  * baseline for the serialized form of Swing objects.
  44.  *
  45.  * @version 1.6 04/10/98
  46.  * @author James Gosling
  47.  * @author Amy Fowler
  48.  * @author Tom Santos
  49.  */
  50. public class BasicHSVChooserPanel extends ColorChooserPanel implements PropertyChangeListener, Serializable {
  51.     // Note: layout manager code was lifted directly from Jame's
  52.     // original color chooser; it really needs to be re-written
  53.     // to handle sizing better and to NOT rely on the order of
  54.     // children added in laying things out.
  55.     //
  56.  
  57.     static int preferredWidth = 300;
  58.     static int preferredHeight = 200;
  59.     static Dimension preferredSize = new Dimension(preferredWidth, preferredHeight);
  60.     static int butGap = 2;
  61.  
  62.     protected Spinner hue, saturation, brightness;
  63.     protected ColorPatch resultColor;
  64.     protected HueLightnessPatch hlp;
  65.     protected ColorWheel wheel;
  66.  
  67.     Color color = Color.red;
  68.  
  69.     public BasicHSVChooserPanel() {
  70.         super();
  71.     }
  72.  
  73.     public BasicHSVChooserPanel( Color startColor ) {
  74.         color = startColor;
  75.     }    
  76.  
  77.     public int[] getHSBColor() {
  78.         Color color = getColor();
  79.     int[] result = new int[ 3 ];
  80.         float[] hsb = Color.RGBtoHSB( color.getRed(), color.getGreen(), color.getBlue(), null );
  81.  
  82.     result[ 0 ] = (int)(hsb[ 0 ] * 100 + 0.5);
  83.     result[ 1 ] = (int)(hsb[ 1 ] * 100 + 0.5);
  84.     result[ 2 ] = (int)(hsb[ 2 ] * 100 + 0.5);
  85.  
  86.     return result;
  87.     }
  88.  
  89.     public Color getColor() {
  90.         return color;
  91.     }
  92.  
  93.     public void setColor( Color newColor ) {
  94.         color = newColor;
  95.     // Set my variables
  96.     float[] hsbColor = Color.RGBtoHSB( color.getRed(), color.getGreen(), color.getBlue(), null );
  97.     int rgbColor = Color.HSBtoRGB( hsbColor[ 0 ], hsbColor[ 1 ], hsbColor[ 2 ] );
  98.     hlp.setColor( rgbColor );
  99.     setSpinners( hsbColor );
  100.     wheel.setTheta( hsbColor[ 0 ] * 360 + 0.5 );
  101.     resultColor.setColor( rgbColor );
  102.     }
  103.        
  104.     /**
  105.      * The background color, foreground color, and font are already set to the
  106.      * defaults from the defaults table before this method is called.
  107.      */                                    
  108.     public void installChooserPanel() {
  109.         // Currently this UI implementation really hard-codes
  110.         // and depends-on all these subcomponents, therefore,
  111.         // for now, we won't break these out into overridable
  112.         // subcomponent creation methods.
  113.  
  114.         setLayout(new ColorChooserLayout());
  115.       
  116.     int[] hsb = getHSBColor();
  117.  
  118.         // Create right-hand color patch to display current color
  119.     resultColor = new ColorPatch();
  120.         resultColor.setBorder(UIManager.getBorder("ColorChooser.selectedColorBorder"));
  121.         //resultColor.addPropertyChangeListener(this);
  122.  
  123.         // Create spinners to display HSB
  124.         hue = new Spinner(hsb[ 0 ], "\u00B0 H");
  125.     hue.setMinimum(0);
  126.     hue.setMaximum(100);
  127.  
  128.         saturation = new Spinner(hsb[ 1 ], "% S");
  129.     saturation.setMinimum(0);
  130.     saturation.setMaximum(100);
  131.  
  132.     brightness = new Spinner(hsb[ 2 ], "% B");
  133.     brightness.setMinimum(0);
  134.     brightness.setMaximum(100);
  135.  
  136.         // Create hue lightness patch (in center of color wheel)
  137.     hlp = new HueLightnessPatch(resultColor, hue, saturation, brightness);
  138.  
  139.         // Note: Order of added children critical to layout manager!
  140.     add(hlp); // child0
  141.  
  142.     saturation.addAdjustmentListener(hlp);
  143.     brightness.addAdjustmentListener(hlp);
  144.  
  145.         // Create color wheel
  146.     wheel = new ColorWheel(hlp, hue);
  147.         hue.setWrap(true);
  148.         add(wheel); // child1
  149.       
  150.     add(resultColor); //child2
  151.     
  152.     add(hue); //child3
  153.     add(saturation); //child4
  154.     add(brightness); //child5 
  155.  
  156.         resultColor.addPropertyChangeListener( this );
  157.     }
  158.  
  159.     public void uninstallChooserPanel() {
  160.         resultColor.removePropertyChangeListener( this );
  161.  
  162.         setLayout(null);
  163.         remove(hlp);
  164.         remove(wheel);
  165.         remove(resultColor);
  166.         remove(hue);
  167.         remove(saturation);
  168.         remove(brightness);
  169.         hlp = null;
  170.         wheel = null;
  171.         resultColor = null;
  172.         hue = saturation = brightness = null;
  173.     }
  174.  
  175.     public void propertyChange(PropertyChangeEvent e) {
  176.     if ( e.getSource() == resultColor && e.getPropertyName().equals( "color" ) ) {
  177.         Color newColor = (Color)e.getNewValue();
  178.         Color oldColor = getColor();
  179.         fireColorPropertyChange( oldColor, newColor );
  180.     }
  181.     }
  182.  
  183.     protected void setSpinners(float hsb[]) {
  184.         float hueValue = hue.getValue();
  185.         float satValue = saturation.getValue();
  186.         float brightValue = brightness.getValue();
  187.         if (hueValue != hsb[0]) {
  188.         hue.setValue((int) (hsb[0] * 360 + 0.5));
  189.         }
  190.         if (satValue != hsb[1]) {
  191.         saturation.setValue((int) (hsb[1] * 100 + 0.5));
  192.         }
  193.         if (brightValue != hsb[2]) {
  194.         brightness.setValue((int) (hsb[2] * 100 + 0.5));
  195.         }
  196.     }
  197.  
  198.     class ColorChooserLayout implements LayoutManager, Serializable {
  199.  
  200.         public void addLayoutComponent(String name, Component comp) { }
  201.         public void removeLayoutComponent(Component comp) { }
  202.  
  203.         public Dimension preferredLayoutSize(Container cont) {
  204.         return preferredSize;
  205.         }
  206.  
  207.         public Dimension minimumLayoutSize(Container cont) {
  208.             return preferredLayoutSize(cont);
  209.         }
  210.  
  211.         public void layoutContainer(Container cont) {
  212.         int ncomp = cont.getComponentCount();
  213.         int butWidth = 16;
  214.         Rectangle r = cont.getBounds();
  215.             Insets insets = cont.getInsets();
  216.         int h = r.height - insets.top - insets.bottom;
  217.         int w = r.width - insets.left - insets.right;
  218.             int xoffset = insets.left;
  219.             int yoffset = insets.top;
  220.  
  221.         for (int i = 3; i < ncomp; i++) {
  222.             Dimension dim = cont.getComponent(i).getMinimumSize();
  223.             if (dim.width > butWidth)
  224.             butWidth = dim.width;
  225.         }
  226.             // compute diameter for color wheel
  227.         int diameter = h;
  228.  
  229.         int r3 = w - butWidth - butGap;
  230.         if (r3 < diameter)
  231.             diameter = r3;
  232.         butWidth = w - diameter - butGap;
  233.  
  234.         // inside saturation/lightness square
  235.         Component c = cont.getComponent(0);
  236.         r3 = (int) (diameter * (.75 * .71));
  237.         c.setBounds(xoffset + ((diameter - r3) >> 1), yoffset + ((diameter - r3) >> 1), 
  238.                         r3, r3);
  239.  
  240.         // outside wheel
  241.         c = cont.getComponent(1);
  242.         c.setBounds(xoffset, yoffset + ((h - diameter) >> 1), diameter, diameter);
  243.  
  244.         int y = 0;
  245.         for (int i = 3; i < ncomp; i++) {
  246.             c = cont.getComponent(i);
  247.             int dh = c.getMinimumSize().height;
  248.             c.setBounds(xoffset + w - butWidth, yoffset + y, butWidth, dh);
  249.             y += dh + butGap;
  250.         }
  251.  
  252.         // colorPatch on the side
  253.         c = cont.getComponent(2);
  254.         c.setBounds(xoffset + w - butWidth, yoffset + y, butWidth, h - y);
  255.         }
  256.     }
  257. }
  258.  
  259.  
  260. class ImageComponent extends JComponent implements ImageObserver {
  261.     protected Image img;
  262.     private boolean imgKnown;
  263.     protected Dimension isize = new Dimension(-1, -1);
  264.     protected Dimension prefsize = new Dimension(0, 0);
  265.     boolean fixedSize = false;
  266.     long lastUpdateTime = 0;
  267.  
  268.     public ImageComponent () {
  269.     super();
  270.     }
  271.     public ImageComponent (Image img) {
  272.     super();
  273.     setImage(img);
  274.     }
  275.     public ImageComponent (String name) {
  276.     super();
  277.     setImage(getToolkit().getImage(name));
  278.     }
  279.     public void setSize(int w, int h) {
  280.     if (w != isize.width || h != isize.height) {
  281.         isize.width = w;
  282.         isize.height = h;
  283.         invalidate();
  284.     }
  285.     }
  286.     public void setBounds(int x, int y, int width, int height) {
  287.     super.setBounds(x, y, width, height);
  288.     setSize(width, height);
  289.     }
  290.     public void setImage(Image img) {
  291.     if (this.img == img)
  292.         return;
  293.     this.img = img;
  294.     if (img != null)
  295.         setSize(img.getWidth(this), img.getHeight(this));
  296.     if (isShowing())
  297.         repaint(10);
  298.     }
  299.     private synchronized void waitSize() {
  300.     if (img != null)
  301.         try {
  302.         while (isize.width < 0 || isize.height < 0)
  303.         wait();
  304.         } catch(InterruptedException i) {
  305.         }
  306.     }
  307.     protected boolean immediatePaint() {
  308.         //(PENDING) STEVE - THIS IS A HACK
  309.     return isShowing();
  310.     // return true;
  311.     }
  312.     public synchronized boolean imageUpdate(Image img,
  313.                         int infoflags,
  314.                         int x, int y,
  315.                         int width, int height) {
  316.     long t = System.currentTimeMillis();
  317.     if ( /* lastUpdateTime+500<t || */ (infoflags & (ALLBITS | FRAMEBITS)) != 0) {
  318.         if (immediatePaint()) {
  319.         Graphics g = getGraphics();
  320.         if ( g != null ) {
  321.             paint(g);
  322.             g.dispose();
  323.         }
  324.         } else
  325.         repaint( /* 1 */ 0);
  326.         lastUpdateTime = t;
  327.     }
  328.     if ((infoflags & (ERROR | ABORT)) != 0) {
  329.         img = null;
  330.         isize.width = 0;
  331.         isize.height = 0;
  332.         notifyAll();
  333.         return false;
  334.     }
  335.     if ((infoflags & WIDTH) != 0)
  336.         isize.width = img.getWidth(this);
  337.     if ((infoflags & HEIGHT) != 0)
  338.         isize.height = img.getHeight(this);
  339.     notifyAll();
  340.     return (infoflags & ALLBITS) == 0;
  341.     }
  342.     public Dimension getPreferredSize() {
  343.         Insets insets = getInsets();
  344.     waitSize();
  345.         prefsize.width = isize.width + insets.left + insets.right;
  346.         prefsize.height = isize.height + insets.top + insets.bottom;
  347.     return prefsize;
  348.     }
  349.     public Dimension getMinimumSize() {
  350.     return getPreferredSize();
  351.     }
  352.     protected void locateImage() {
  353.     };
  354.     public void paint(Graphics g) {
  355.         Insets insets = getInsets();
  356.     Image i = img;
  357.     if (i == null) {
  358.         locateImage();
  359.         if ((i = img) == null)
  360.         return;
  361.     }
  362. if ( g == null ) System.out.println( "NULL GRAPHICS" );
  363. if ( insets == null ) System.out.println( "NULL INSETS" );
  364.     g.drawImage(i, insets.left, insets.top, this);
  365.     }
  366.     public void update(Graphics g) {
  367.     paint(g);
  368.     }
  369. }
  370.  
  371. class ColorWheel extends ImageComponent implements AdjustmentListener {
  372.     private static Image img;
  373.     SyntheticImage vSrc;
  374.     int sx, sy;
  375.     private static int cursorWidth = 3;
  376.     HueLightnessPatch hlp;
  377.     Adjustable hue;
  378.  
  379.     ColorWheel (HueLightnessPatch hlp, Adjustable hue) {
  380.     super();
  381.     this.hlp = hlp;
  382.         this.hue = hue;
  383.     hue.setMinimum(0);
  384.     hue.setMaximum(359);
  385.         hue.addAdjustmentListener(this);
  386.     enableEvents(AWTEvent.MOUSE_EVENT_MASK
  387.              | AWTEvent.MOUSE_MOTION_EVENT_MASK);
  388.     }
  389.  
  390.     protected void processMouseEvent(MouseEvent e) {
  391.     switch (e.getID()) {
  392.       case MouseEvent.MOUSE_RELEASED:
  393.         // moveSel(e.getX(), e.getY());
  394.         // if (continuous) notfyListener();
  395.         // break;
  396.       case MouseEvent.MOUSE_PRESSED:
  397.       case MouseEvent.MOUSE_DRAGGED:
  398.         moveSel(e.getX(), e.getY());
  399.     }
  400.     }
  401.  
  402.     protected void processMouseMotionEvent(MouseEvent e) {
  403.     processMouseEvent(e);
  404.     }
  405.  
  406.     public void setTheta( double newTheta ) {
  407.         lastCursorAngle = (int)newTheta;
  408.     repaint();
  409.     }
  410.  
  411.     protected void locateImage() {
  412.     if (img == null) {
  413.         final Dimension s = getSize();
  414.         vSrc = new SyntheticImage() {
  415.         {
  416.             this.width = s.width;
  417.             this.height = s.height;
  418.         }
  419.         protected void computeRow(int y, int[] row) {
  420.             int r = width >> 1;
  421.             int r2 = r * 3 / 4;
  422.             int dy = y - r;
  423.             for (int x = width; --x >= 0;) {
  424.             int dx = x - r;
  425.             int dr = (int) (Math.sqrt(dy * dy + dx * dx + dx + dy + 0.5) + 0.5);
  426.             if (dr >= r || dr <= r2)
  427.                 row[x] = 0;
  428.             else {
  429.                 float brightness = 1.0f;
  430.                 float saturation = 1.0f;
  431.                 int theta = (int) (Math.atan2(dx, dy) * (180 / Math.PI));
  432.                 if (theta < 0)
  433.                 theta += 360;
  434.                 if (dr >= r - 2 || dr <= r2 + 2) {
  435.                 int lt = theta + (360 - 45);
  436.                 if (lt >= 360)
  437.                     lt -= 360;
  438.                 if (lt >= 180)
  439.                     lt = 360 - lt;
  440.                 lt = lt - 90;
  441.                 if (dr <= r2 + 2)
  442.                     lt = -lt;
  443.                 if (lt < 0)
  444.                     brightness = 1.0f - lt / (float) (-180);
  445.                 else
  446.                     saturation = 1.0f - lt / (float) 120;
  447.                 }
  448.                 row[x] = Color.HSBtoRGB(theta / ((float) 360.0),
  449.                               saturation, brightness);
  450.             }
  451.             }
  452.         }
  453.         };
  454.         setImage(getToolkit().createImage(vSrc));
  455.     }
  456.     }
  457.     int lastCursorAngle = -1;
  458.     public void adjustmentValueChanged(AdjustmentEvent e) {
  459.     int theta = e.getValue();
  460.     hlp.setColor(Color.HSBtoRGB(theta / ((float) 360.0), 1, 1));
  461.     hlp.propogateColor();
  462.     Graphics g = getGraphics();
  463.     if (g != null && isShowing()) {  // PENDING (STEVE) the isShowing() thing is a hack
  464.         paintCursor(g, lastCursorAngle);
  465.         paintCursor(g, lastCursorAngle = theta);
  466.         g.dispose();
  467.     }
  468.     }
  469.     private void moveSel(int tx, int ty) {
  470.     if (tx == sx && ty == sy)
  471.         return;
  472.     sx = tx;
  473.     sy = ty;
  474.     int dx = sx - (isize.width >> 1);
  475.     int dy = sy - (isize.height >> 1);
  476.     int theta = (int) (Math.atan2(dx, dy) * (180 / Math.PI));
  477.     if (theta < 0)
  478.         theta += 360;
  479.     hue.setValue(theta);
  480.     }
  481.     public void paint(Graphics g) {
  482.     super.paint(g);
  483.     paintCursor(g, lastCursorAngle);
  484.     }
  485.     private void paintCursor(Graphics g, int theta) {
  486.     if (theta < 0)
  487.         return;
  488.     g.setColor(Color.white);
  489.     g.setXORMode(Color.black);
  490.     int xc = isize.width >> 1;    // Center
  491.     int yc = isize.height >> 1;
  492.     int xr = (int) (Math.sin(theta * (Math.PI / 180)) * xc);    // Radius vector
  493.     int yr = (int) (Math.cos(theta * (Math.PI / 180)) * yc);
  494.     g.drawArc(xc + xr - (xr >> 3) - (xc >> 4),
  495.           yc + yr - (yr >> 3) - (yc >> 4),
  496.           xc >> 3, xc >> 3, 0, 360);
  497.     }
  498.     public void update(Graphics g) {
  499.     paint(g);
  500.     }
  501. }
  502.  
  503. class HueLightnessPatch extends ImageComponent implements AdjustmentListener {
  504.     int sx, sy;
  505.     private static int cursorWidth = 3;
  506.     SyntheticImage hli;
  507.     ColorPatch target;
  508.     Adjustable hue;
  509.     Adjustable saturation;
  510.     Adjustable brightness;
  511.     int color;
  512.     HueLightnessPatch(ColorPatch target, 
  513.                       Adjustable hue, Adjustable saturation, Adjustable brightness) {
  514.     super();
  515.     this.target = target;
  516.     this.hue = hue;
  517.         this.saturation = saturation;
  518.         this.brightness = brightness;
  519.     target.setColor(0xFF0000);
  520.     enableEvents(AWTEvent.MOUSE_EVENT_MASK
  521.              | AWTEvent.MOUSE_MOTION_EVENT_MASK);
  522.     }
  523.     protected void locateImage() {
  524.     if (img == null) {
  525.         final Dimension s = getSize();
  526.         final int c0 = color;
  527.             final Adjustable imageHue = hue;
  528.         hli = new SyntheticImage() {
  529.         int color, nextColor;
  530.                 Adjustable hue;
  531.         boolean changed;
  532.         {
  533.                     this.hue = imageHue;
  534.             this.width = s.width;
  535.             this.height = s.height;
  536.             nextFrame(c0);
  537.         }
  538.         public synchronized void nextFrame(int c) {
  539.             c |= pixMask << 24;
  540.             if (color != c) {
  541.             changed = true;
  542.             notifyAll();
  543.             }
  544.             nextColor = c;
  545.         }
  546.         private synchronized void waitNextColor() {
  547.             try {
  548.             while (!changed)
  549.                 wait();
  550.             }
  551.             catch(InterruptedException e) {
  552.             }
  553.             changed = false;
  554.             color = nextColor;
  555.         }
  556.         protected boolean isStatic() {
  557.             return false;
  558.         }
  559.         protected void computeRow(int y, int[] row) {
  560.             if (y == 0)
  561.             waitNextColor();
  562.             float theta = hue.getValue() / ((float) 360.0);
  563.             float brightness = (height - y - 1) / (float) (height - 1);
  564.             int w = width;
  565.             for (int i = w; --i >= 0;) {
  566.             float sat = (w - i - 1) / (float) (w - 1);
  567.             row[i] = Color.HSBtoRGB(theta, sat, brightness);
  568.             }
  569.         }
  570.         };
  571.         setImage(getToolkit().createImage(hli));
  572.     }
  573.     }
  574.     protected void processMouseEvent(MouseEvent e) {
  575.     switch (e.getID()) {
  576.       case MouseEvent.MOUSE_PRESSED:
  577.       case MouseEvent.MOUSE_RELEASED:
  578.       case MouseEvent.MOUSE_DRAGGED:
  579.         int h = isize.height;
  580.         int w = isize.width;
  581.         brightness.setValue((h - e.getY() - 1) * 100 / (h - 1));
  582.         saturation.setValue((w - e.getX() - 1) * 100 / (w - 1));
  583.     }
  584.     }
  585.     public void setColor(int c) {
  586.     color = c;
  587.     if (hli != null)
  588.         hli.nextFrame(c);
  589.     }
  590.     protected void processMouseMotionEvent(MouseEvent e) {
  591.     processMouseEvent(e);
  592.     }
  593.     public void adjustmentValueChanged(AdjustmentEvent e) {
  594.     int h = isize.height - 1;
  595.     int w = isize.width - 1;
  596.     int tx = w - saturation.getValue() * w / 100;
  597.     int ty = h - brightness.getValue() * h / 100;
  598.     Graphics g = getGraphics();
  599.     if (g != null && isShowing()) {  // PENDING (STEVE) the isShowing() thing is a hack
  600.         paintCursor(g, sx, sy);
  601.         paintCursor(g, tx, ty);
  602.         g.dispose();
  603.     }
  604.     sx = tx;
  605.     sy = ty;
  606.     propogateColor();
  607.     }
  608.     void propogateColor() {
  609.     int h = isize.height;
  610.     int w = isize.width;
  611.     target.setColor(Color.HSBtoRGB(hue.getValue() / ((float) 360.0),
  612.                  saturation.getValue() / ((float) 100.0),
  613.                brightness.getValue() / ((float) 100.0)));
  614.     }
  615.     public void paint(Graphics g) {
  616.     super.paint(g);
  617.     paintCursor(g, sx, sy);
  618.     }
  619.     private void paintCursor(Graphics g, int sx, int sy) {
  620.     g.setColor(Color.white);
  621.     g.setXORMode(Color.black);
  622.     g.drawLine(sx - 3, sy, sx + 3, sy);
  623.     g.drawLine(sx, sy - 3, sx, sy + 3);
  624.     }
  625.     public void update(Graphics g) {
  626.     paint(g);
  627.     }
  628. }
  629.  
  630.     class ColorPatch extends ImageComponent {
  631.         SyntheticImage vSrc;
  632.         int patchColor;
  633.         protected void locateImage() {
  634.         if (img == null) {
  635.             final Dimension s = getSize();
  636.         final int c0 = patchColor;
  637.         vSrc = new SyntheticImage() {
  638.             int color, nextColor;
  639.             boolean changed;
  640.             {
  641.                 this.width = s.width;
  642.             this.height = s.height;
  643.             nextFrame(c0);
  644.             }
  645.             public synchronized void nextFrame(int c) {
  646.                 c |= pixMask << 24;
  647.             if (color != c) {
  648.                 changed = true;
  649.                 notifyAll();
  650.             }
  651.             nextColor = c;
  652.             }
  653.             private synchronized void waitNextColor() {
  654.                 try {
  655.                 while (!changed)
  656.                     wait();
  657.             }
  658.             catch(InterruptedException e) {
  659.             }
  660.             changed = false;
  661.             color = nextColor;
  662.             }
  663.             protected boolean isStatic() {
  664.                 return false;
  665.             }
  666.             protected void computeRow(int y, int[] row) {
  667.                 if (y == 0)
  668.                 waitNextColor();
  669.             int C = color;
  670.             for (int i = row.length; --i >= 0;)
  671.                 row[i] = C;
  672.             }
  673.         };
  674.         setImage(getToolkit().createImage(vSrc));
  675.         }
  676.     }
  677.         public void setColor(int c) {
  678.         int old = patchColor;
  679.         patchColor = c;
  680.         if (vSrc != null)
  681.             vSrc.nextFrame(c);
  682.         if (old != c) {
  683.         firePropertyChange( "color", new Color( old ), new Color( patchColor ) );
  684.         }
  685.     }
  686.     }
  687.